#include "timer.h"

#include <utility>

namespace yuechuan {
    bool Timer::Comparator::operator()(const Timer::ptr &lhs, const Timer::ptr &rhs) const {
        if (!lhs && !rhs) {
            return false;
        }
        if (!lhs) {
            return true;
        }
        if (!rhs) {
            return false;
        }
        if (lhs->m_next < rhs->m_next) {
            return true;
        }
        if (lhs->m_next > rhs->m_next) {
            return false;
        }
        return lhs.get() < rhs.get();
    }

    Timer::Timer(uint64_t ms, std::function<void()> cb, bool recurring, TimerManager *manager)
            : m_recurring(recurring), m_ms(ms), m_cb(std::move(cb)), m_manager(manager) {
        m_next = GetCurrentTimeMS() + m_ms;
    }

    Timer::Timer(uint64_t next)
            : m_next(next) {

    }

    bool Timer::cancel() {
        TimerManager::RWMutexType::WriteLock lock(m_manager->m_mutex);
        if (m_cb) {
            m_cb = nullptr;
            auto it = m_manager->m_timers.find(shared_from_this());
            m_manager->m_timers.erase(it);
            lock.unlock();
            return true;
        }
        lock.unlock();
        return false;
    }

    bool Timer::refresh() {
        TimerManager::RWMutexType::WriteLock lock(m_manager->m_mutex);
        if (!m_cb) {
            return false;
        }
        auto it = m_manager->m_timers.find(shared_from_this());
        if (it == m_manager->m_timers.end()) {
            return false;
        }
        m_manager->m_timers.erase(it);
        m_next = GetCurrentTimeMS() + m_ms;
        m_manager->m_timers.insert(shared_from_this());
        return true;
    }

    bool Timer::reset(uint64_t ms, bool from_now) {
        if (ms == m_ms && !from_now) {
            return true;
        }
        TimerManager::RWMutexType::WriteLock lock(m_manager->m_mutex);
        if (!m_cb) {
            return false;
        }
        auto it = m_manager->m_timers.find(shared_from_this());
        if (it == m_manager->m_timers.end()) {
            return false;
        }
        m_manager->m_timers.erase(it);
        uint64_t start = 0;
        if (from_now) {
            start = GetCurrentTimeMS();
        } else {
            start = m_next - m_ms;
        }
        m_ms = ms;
        m_next = start + m_ms;
        m_manager->addTimer(shared_from_this(), lock);
        return true;
    }

    TimerManager::TimerManager() {
        m_previousTime = GetCurrentTimeMS();
    }

    TimerManager::~TimerManager() {

    }

    Timer::ptr TimerManager::addTimer(uint64_t ms, std::function<void()> cb, bool recurring) {
        Timer::ptr timer(new Timer(ms, cb, recurring, this));
        RWMutexType::WriteLock lock(m_mutex);
        addTimer(timer, lock);
        return timer;
    }

    void TimerManager::addTimer(Timer::ptr timer, RWMutexType::WriteLock &lock) {
        /// insert 返回值是一个std::pair (插入的位置, 是否成功)
        auto it = m_timers.insert(timer).first;
        bool at_front = (it == m_timers.begin()) && !m_tickled;
        if (at_front) {
            m_tickled = true;
        }
        lock.unlock();
        if (at_front) {
            onTimerInsertForFront();
        }
    }

    /*
     * addConditionTimer中用于绑定的函数
     * 弱指针的使用
    */
    static void OnTimer(std::weak_ptr<void> weak_cond, std::function<void()> cb) {
        std::shared_ptr<void> tmp = weak_cond.lock();
        if (tmp) {
            cb();
        }
    }

    /*
     * 添加条件定时器
     *
     * 采用弱指针的方式
     * 即不会因为引用加一
     * 绑定一个函数，当弱指针还存在时就会触发相应的回调函数
    */
    Timer::ptr TimerManager::addConditionTimer(uint64_t ms, std::function<void()> cb, std::weak_ptr<void> weak_cond,
                                               bool recurring) {
        return addTimer(ms, std::bind(&OnTimer, weak_cond, cb), recurring);
    }

    void TimerManager::listExpiredCb(std::vector<std::function<void()> > &cbs) {
        uint64_t now_ms = GetCurrentTimeMS();
        /// 已经超时的数组
        std::vector<Timer::ptr> expired;
        {
            RWMutexType::ReadLock lock(m_mutex);
            if (m_timers.empty()) {
                return;
            }
        }

        if (m_timers.empty()) {
            return;
        }
        RWMutexType::WriteLock lock(m_mutex);
        bool rollover = detectClockRollover(now_ms);
        if (!rollover && (*m_timers.begin())->m_next > now_ms) {
            return;
        }
        Timer::ptr now_timer(new Timer(now_ms));
        auto it = rollover ? m_timers.end() : m_timers.lower_bound(now_timer);
        while (it != m_timers.end() && (*it)->m_next == now_ms) {
            ++it;
        }
        expired.insert(expired.begin(), m_timers.begin(), it);
        m_timers.erase(m_timers.begin(), it);
        cbs.reserve(expired.size());

        for (auto &timer: expired) {
            cbs.push_back(timer->m_cb);
            if (timer->m_recurring) {
                timer->m_next = now_ms + timer->m_ms;
                m_timers.insert(timer);
            } else {
                timer->m_cb = nullptr;
            }
        }

         lock.unlock();
    }


    /*
     * 对于epoll_wait要知道下一个定时器下一个周期时间
     * 当获得当前时间大于等待队列中的开始时间，表示该时间已经要立马执行了
    */
    uint64_t TimerManager::getNextTimer() {
        RWMutexType::ReadLock lock(m_mutex);
        m_tickled = false;
        if (m_timers.empty()) {
            return ~0ull;
        }

        const Timer::ptr &next = *m_timers.begin();
        uint64_t now_ms = GetCurrentTimeMS();
        if (now_ms >= next->m_next) {

            return 0;
        } else {
            return next->m_next - now_ms;
        }
    }

    /*
     * 检测服务器时间是否被调后
     *
     * 默认是正常时间
     * 当发现当前服务器时间小于上一次执行时间， 并且小于上一次时间多于1小时
     * 可以说明该服务器时间确实被调后了
        * 需要将所有定时器加入到超时队列中等待执行
    */
    bool TimerManager::detectClockRollover(uint64_t now_ms) {
        bool rollover = false;
        if (now_ms < m_previousTime && now_ms < (m_previousTime - 60 * 60 * 1000)) {
            rollover = true;
        }
        m_previousTime = now_ms;
        return rollover;
    }

    bool TimerManager::hasTimer() {
        RWMutexType::ReadLock lock(m_mutex);
        return !m_timers.empty();
    }

} // namespace yuechuan

